package com.ladlee.validata.filter;

import com.ladlee.properties.SecurityConstants;
import com.ladlee.properties.SecurityProperties;
import com.ladlee.validata.ValidataCodeType;
import com.ladlee.validata.ValidateCodeProcessorHolder;
import com.ladlee.validata.exception.ValidateCodeException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @Auther: dingchang
 * @Date: 2018/12/21 19:23
 * @Description: 验证码拦截器，在请求前只拦截一次
 */
@Slf4j
@Component("validataCodeFilter")
public class ValidataCodeFilter extends OncePerRequestFilter implements InitializingBean {

    @Autowired
    private SecurityProperties securityProperties;

    /**
     * 验证码处理器
     */
    @Autowired
    private ValidateCodeProcessorHolder validateCodeProcessorHolder;

    /**
     * 失败处理器
     */
    @Autowired
    private AuthenticationFailureHandler loginFailHandler;

    /**
     * 验证请求url与配置得url是否匹配得工具类
     */
    AntPathMatcher matcher = new AntPathMatcher();

    /**
     * 存放所有需要检验验证码得url
     */
    private Map<String, ValidataCodeType> urlMap = new HashMap<>();

    /**
     * 初始化bena完成后在加该信息也就是拦截得url
     * @throws ServletException
     */
    @Override
    public void afterPropertiesSet() throws ServletException {
        super.afterPropertiesSet();
        //添加拦截图形验证码的url
        urlMap.put(SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_FORM,ValidataCodeType.IMAGE);
        urlToMap(securityProperties.getCode().getImage().getUrl(),ValidataCodeType.IMAGE);

        //添加拦截短信验证码的URL
        urlMap.put(SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_MOBILE,ValidataCodeType.SMS);
        urlToMap(securityProperties.getCode().getSms().getUrl(), ValidataCodeType.SMS);
    }

    /**
     * 将系统中配置的需要校验验证码的URL根据校验的类型放入map
     *
     * @param urlStr
     * @param type
     */
    private void urlToMap(String urlStr, ValidataCodeType type){
        if(StringUtils.isNotBlank(urlStr)){
            String[] urls = StringUtils.splitByWholeSeparatorPreserveAllTokens(urlStr, ",");
            for (String url : urls){
                urlMap.put(url,type);
            }
        }
    }

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        ValidataCodeType type = getValidateCodeType(httpServletRequest);
        if(type != null){
            logger.info("校验请求(" + httpServletRequest.getRequestURI() + ")中的验证码,验证码类型" + type);
            //验证验证码
            try {
                validateCodeProcessorHolder.findValidateCodeProcessor(type).validate(new ServletWebRequest(httpServletRequest,httpServletResponse));
            } catch (ValidateCodeException e) {
                //进行失败处理
                loginFailHandler.onAuthenticationFailure(httpServletRequest,httpServletResponse,e);
                return;
            }
        }
        filterChain.doFilter(httpServletRequest,httpServletResponse);
    }

    /**
     * 获取校验码的类型，如果当前请求不需要校验，则返回null
     */
    private ValidataCodeType getValidateCodeType(HttpServletRequest request){
        ValidataCodeType result = null;
        String method = request.getMethod();
        if(!StringUtils.equalsIgnoreCase(method,"get")){
            Set<String> set = urlMap.keySet();
            for (String url : set){
                if(matcher.match(url,request.getRequestURI())){
                    result = urlMap.get(url);
                }
            }
        }
        return result;
    }

}
